#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <utility>
#include <mutex>
#include "../util/util.hpp"
#include "../log/log.hpp"
namespace bs_index
{
    // 描述文档信息
    struct doc_info_t
    {
        std::string title;   // 文档标题
        std::string content; // 文档内容
        std::string url;     // 文档url
        uint64_t doc_id;     // 文档id
    };
    // 倒排元素
    struct inverted_elem_t
    {
        uint64_t doc_id;      // 文档id
        std::string key_word; // 关键字
        int weight;           // 权重
    };
    // 倒排拉链
    typedef std::vector<inverted_elem_t> inverted_list_t;
    class index_t
    {
    private:
        // 正排索引用动态顺序表 下标天然为文档id
        std::vector<doc_info_t> _forward_index;
        // 倒排索引一定是 一个关键字对应一个/组倒排元素对应[关键字和倒排拉链的映射关系]
        std::unordered_map<std::string, inverted_list_t> _inverted_index;

    private:
        // 单例
        static index_t *_instance;
        // 创建单例时保护线程安全
        static std::mutex _mutex;

    public:
        static index_t *get_instance()
        {
            if (_instance == nullptr)
            {
                _mutex.lock();
                if (_instance == nullptr)
                {
                    _instance = new index_t;
                }
                _mutex.unlock();
            }
            return _instance;
        }
        index_t() { ; };
        index_t(index_t &index) = delete;
        index_t &operator=(index_t &index) = delete;
        ~index_t() { ; };

    public:
        // 根据文档id 通过正排索引获取文档信息
        doc_info_t *get_doc_info(const uint64_t doc_id)
        {
            if (doc_id >= _forward_index.size())
            {
                boost_searcher::searcher_log(WARNING, "doc_id is out of range!");
                return nullptr;
            }
            return &_forward_index[doc_id];
        }
        // 根据关键字 通过倒排索引获取倒排拉链(包含文档id)
        inverted_list_t *get_inverted_list(const std::string key_word)
        {
            auto iter = _inverted_index.find(key_word);
            if (iter == _inverted_index.end())
            {
                boost_searcher::searcher_log(WARNING, "not found in inverted_index word:" + key_word);
                return nullptr;
            }
            return &(iter->second);
        }
        // 根据完成数据清洗后的html文件 构建正派和倒排索引
        bool build_index(const std::string &file_path)
        {
            // 创建输入文件流
            std::ifstream ifs(file_path, std::ios_base::in | std::ios_base::binary);
            if (!ifs.is_open())
            {
                boost_searcher::searcher_log(FATAL, "failed to open input file! file name:" + file_path);
                return false;
            }
            // 按行读取文档信息
            std::string line;
            size_t count = 0;
            while (std::getline(ifs, line))
            {
                // 动态建立正排索引
                doc_info_t *p_doc = build_forward_index(line);
                if (p_doc == nullptr)
                {
                    boost_searcher::searcher_log(WARNING, "line is corrupted");
                    continue;
                }
                // 动态完善倒排索引
                build_inverted_index(*p_doc);
                count++;
                if (!(count % 50))
                {
                    boost_searcher::searcher_log(NORMAL, "building index: " + std::to_string(count));
                }
            }
            return true;
        }

    private:
        doc_info_t *build_forward_index(const std::string &line)
        {
            // 解析line
            std::vector<std::string> parse_res;
            const std::string sep("\3"); // 行内分隔符
            bs_util::string_util::split(line, parse_res, sep);
            if (parse_res.size() != 3)
            {
                boost_searcher::searcher_log(WARNING, "failed to parser line!");
                return nullptr;
            }
            // 填充doc_info
            doc_info_t doc_info;
            doc_info.title.swap(parse_res[0]);
            doc_info.content.swap(parse_res[1]);
            doc_info.url.swap(parse_res[2]);
            doc_info.doc_id = _forward_index.size();
            // 插入正排索引
            _forward_index.push_back(std::move(doc_info));
            // 返回新插入的元素地址
            return &_forward_index.back();
        }
        void build_inverted_index(const doc_info_t &doc_info)
        {
            // 分词 + 词频统计
            struct word_count_t
            {
                int title_count;
                int content_count;
            };
            std::unordered_map<std::string, word_count_t> count_map;
            // 统计标题词频
            std::vector<std::string> title_words;
            bs_util::jieba_util::cut_string(doc_info.title, title_words);
            for (auto &e : title_words)
            {
                // 统一转化为小写
                boost::to_lower(e);
                count_map[e].title_count++;
            }
            // 统计内容词频
            std::vector<std::string> content_words;
            bs_util::jieba_util::cut_string(doc_info.content, content_words);
            for (auto &e : content_words)
            {
                // 统一转化为小写
                boost::to_lower(e);
                count_map[e].content_count++;
            }
// 自定义相关性 (词频权重)
#define title_weight 30
#define content_weight 1
            // 构建倒排索引
            for (auto &e : count_map)
            {
                inverted_elem_t inverted_elem;
                inverted_elem.doc_id = doc_info.doc_id;
                inverted_elem.key_word = e.first;
                inverted_elem.weight = title_weight * e.second.title_count + content_weight * e.second.content_count;
                // 往倒排拉链中持续添加元素
                _inverted_index[e.first].push_back(std::move(inverted_elem));
            }
        }
    };
    // 初始化单例
}
bs_index::index_t *bs_index::index_t::_instance = nullptr;
std::mutex bs_index::index_t::_mutex;
